package cn.koala.key.common.utils;

import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 实体工具类。
 *
 * @author liutao
 * @date 2020/8/18
 */
public abstract class BaseEntityUtil {

    /**
     * 取得对象去重集合。
     *
     * @param col         包含数据对象的集合。传入null等价于传入空集合。将自动忽略所有null元素。
     * @param valueMapper 值Mapper。禁止传入null。
     * @param <T>         对象类型。
     * @param <V>         值类型。
     * @return 返回{@code Set}集合，其中value由{@code valueMapper}产生。正常情况下不会返回null。
     */
    public static <T, V> Set<V> toSet(Collection<T> col, Function<T, V> valueMapper) {
        Assert.notNull(valueMapper, "valueMapper");

        Set<V> values = new HashSet<>();
        if (col == null || col.isEmpty()) {
            return values;
        }
        for (T item : col) {
            V value = valueMapper.apply(item);
            if (value != null) {
                values.add(value);
            }
        }
        return values;
    }

    /**
     * 取得对象集合。
     *
     * @param col         包含数据对象的集合。传入null等价于传入空集合。将自动忽略所有null元素。
     * @param valueMapper 值Mapper。禁止传入null。
     * @param <T>         对象类型。
     * @param <V>         值类型。
     * @return 返回{@code List}集合，其中value由{@code valueMapper}产生。正常情况下不会返回null。
     */
    public static <T, V> List<V> toList(Collection<T> col, Function<T, V> valueMapper) {
        Assert.notNull(valueMapper, "valueMapper");

        List<V> values = new ArrayList<>();
        if (CollectionUtils.isEmpty(col)) {
            return values;
        }
        for (T item : col) {
            V value = valueMapper.apply(item);
            if (value != null) {
                values.add(value);
            }
        }
        return values;
    }

    /**
     * 取得对象映射表。
     *
     * @param col       数据对象集合。传入null等价于传入空集合。忽略所有null元素。
     * @param keyMapper 映射表键Mapper。禁止传入null。
     * @param <T>       对象类型。
     * @param <K>       映射表键类型。
     * @return 返回映射表，其中key由{@code keyMapper}产生，value为对应的对象，不包含未找到的数据。正常情况下不会返回null。
     * @throws IllegalArgumentException 当参数keyMapper为null时抛出。<br>
     */
    public static <T, K> Map<K, T> toMap(Collection<T> col, Function<T, K> keyMapper) {
        return toMap(col, keyMapper, item -> item);
    }

    /**
     * 取得对象映射表。
     *
     * @param col         数据对象集合。传入null等价于传入空集合。忽略所有null元素。
     * @param keyMapper   映射表键Mapper。禁止传入null。
     * @param valueMapper 映射表值Mapper。禁止传入null。
     * @param <T>         对象类型。
     * @param <K>         映射表键类型。
     * @param <V>         映射表值类型。
     * @return 返回映射表，其中key由{@code keyMapper}产生，value由{@code valueMapper}产生，不包含未找到的数据。正常情况下不会返回null。
     * @throws IllegalArgumentException 当参数keyMapper为null时抛出。<br>
     *                                  当参数valueMapper为null时抛出。<br>
     */
    public static <T, K, V> Map<K, V> toMap(Collection<T> col, Function<T, K> keyMapper,
                                            Function<T, V> valueMapper) {
        Assert.notNull(keyMapper, "keyMapper");
        Assert.notNull(valueMapper, "valueMapper");

        if (col == null || col.isEmpty()) {
            return new HashMap<>();
        }

        return col.stream().filter(Objects::nonNull)
                .collect(Collectors.toMap(keyMapper, valueMapper, (t, u) -> u));
    }

    /**
     * 过滤对象并取得对象集合。
     *
     * @param col       包含数据对象的集合。传入null等价于传入空集合。将自动忽略所有null元素。
     * @param predicate 过滤条件
     * @param <V>       对象类型。
     * @return 返回{@code List}集合，其中value由{@code valueMapper}产生。正常情况下不会返回null。
     */
    public static <V> List<V> toFilterList(Collection<V> col, Predicate<? super V> predicate) {
        return toFilterList(col, predicate, item -> item);
    }

    /**
     * 过滤对象并取得对象集合。
     *
     * @param col         包含数据对象的集合。传入null等价于传入空集合。将自动忽略所有null元素。
     * @param valueMapper 值Mapper。禁止传入null。
     * @param <T>         对象类型。
     * @param <V>         值类型。
     * @return 返回{@code List}集合，其中value由{@code valueMapper}产生。正常情况下不会返回null。
     */
    public static <T, V> List<V> toFilterList(Collection<T> col, Predicate<? super T> predicate,
                                              Function<T, V> valueMapper) {
        Assert.notNull(predicate, "predicate");
        Assert.notNull(valueMapper, "valueMapper");

        List<V> values = new ArrayList<>();
        if (CollectionUtils.isEmpty(col)) {
            return values;
        }

        return col.stream().filter(predicate).map(valueMapper).collect(Collectors.toList());
    }

    /**
     * 过滤对象取得对象去重集合。
     *
     * @param col         包含数据对象的集合。传入null等价于传入空集合。将自动忽略所有null元素。
     * @param predicate   过滤条件
     * @param valueMapper 值Mapper。禁止传入null。
     * @param <T>         对象类型。
     * @param <V>         值类型。
     * @return 返回{@code Set}集合，其中value由{@code valueMapper}产生。正常情况下不会返回null。
     */
    public static <T, V> Set<V> toFilterSet(Collection<T> col, Predicate<? super T> predicate,
                                            Function<T, V> valueMapper) {
        Assert.notNull(predicate, "predicate");
        Assert.notNull(valueMapper, "valueMapper");

        Set<V> values = new HashSet<>();
        if (col == null || col.isEmpty()) {
            return values;
        }
        for (T item : col) {
            if (predicate.test(item)) {
                V value = valueMapper.apply(item);
                if (value != null) {
                    values.add(value);
                }
            }
        }
        return values;
    }

    /**
     * 过滤对象并取得对象映射表。
     *
     * @param col       数据对象集合。传入null等价于传入空集合。忽略所有null元素。
     * @param predicate 过滤条件predicate。禁止传入null。
     * @param keyMapper 映射表键Mapper。禁止传入null。
     * @param <T>       对象类型。
     * @param <K>       映射表键类型。
     * @param <V>       映射表值类型。
     * @return 返回映射表，其中key由{@code keyMapper}产生，value由{@code valueMapper}产生，不包含未找到的数据。正常情况下不会返回null。
     * @throws IllegalArgumentException 当参数keyMapper为null时抛出。<br>
     *                                  当参数valueMapper为null时抛出。<br>
     */
    public static <T, K, V> Map<K, V> toFilterMap(Collection<T> col, Predicate<? super T> predicate,
                                                  Function<T, K> keyMapper) {
        return toFilterMap(col, predicate, keyMapper);
    }

    /**
     * 过滤对象并取得对象映射表。
     *
     * @param col         数据对象集合。传入null等价于传入空集合。忽略所有null元素。
     * @param predicate   过滤条件predicate。禁止传入null。
     * @param keyMapper   映射表键Mapper。禁止传入null。
     * @param valueMapper 映射表值Mapper。禁止传入null。
     * @param <T>         对象类型。
     * @param <K>         映射表键类型。
     * @param <V>         映射表值类型。
     * @return 返回映射表，其中key由{@code keyMapper}产生，value由{@code valueMapper}产生，不包含未找到的数据。正常情况下不会返回null。
     * @throws IllegalArgumentException 当参数keyMapper为null时抛出。<br>
     *                                  当参数valueMapper为null时抛出。<br>
     */
    public static <T, K, V> Map<K, V> toFilterMap(Collection<T> col, Predicate<? super T> predicate,
                                                  Function<T, K> keyMapper, Function<T, V> valueMapper) {
        Assert.notNull(keyMapper, "keyMapper");
        Assert.notNull(valueMapper, "valueMapper");
        Assert.notNull(predicate, "predicate");

        if (col == null || col.isEmpty()) {
            return new HashMap<>();
        }

        return col.stream().filter(predicate)
                .collect(Collectors.toMap(keyMapper, valueMapper, (t, u) -> u));
    }

    /**
     * 集合计算BigDecimal数值总和
     *
     * @param col         数据对象集合。传入null等价于传入空集合。忽略所有null元素。
     * @param valueMapper 值Mapper。禁止传入null。
     * @param <T>         对象类型。
     * @return BigDecimal数值总和
     */
    public static <T> BigDecimal rollUpBigDecimal(Collection<T> col, Function<T, BigDecimal> valueMapper) {
        Assert.notNull(valueMapper, "valueMapper");
        BigDecimal total = BigDecimal.ZERO;
        if (CollectionUtils.isEmpty(col)) {
            return total;
        }
        col.removeIf(Objects::isNull);
        for (T item : col) {
            BigDecimal value = valueMapper.apply(item);
            total = total.add(Optional.ofNullable(value).orElse(BigDecimal.ZERO));
        }
        return total;
    }

    /**
     * 集合计算BigDecimal数值总和
     *
     * @param col         数据对象集合。传入null等价于传入空集合。忽略所有null元素。
     * @param valueMapper1 值Mapper。禁止传入null。
     * @param valueMapper2 值Mapper。禁止传入null。
     * @param <T>         对象类型。
     * @return BigDecimal数值总和
     */
    public static <T> BigDecimal rollUpPairBigDecimal(Collection<T> col, Function<T, BigDecimal> valueMapper1,Function<T, BigDecimal> valueMapper2) {
        Assert.notNull(valueMapper1, "valueMapper1");
        Assert.notNull(valueMapper2, "valueMapper2");
        BigDecimal total = BigDecimal.ZERO;
        if (CollectionUtils.isEmpty(col)) {
            return total;
        }
        col.removeIf(Objects::isNull);
        for (T item : col) {
            BigDecimal value1 = valueMapper1.apply(item);
            BigDecimal value2 = valueMapper2.apply(item);
            total = total.add(Optional.ofNullable(value1).orElse(BigDecimal.ZERO).add(Optional.ofNullable(value2).orElse(BigDecimal.ZERO)));
        }
        return total;
    }

    /**
     * 集合计算Integer数值总和
     *
     * @param col         数据对象集合。传入null等价于传入空集合。忽略所有null元素。
     * @param valueMapper 值Mapper。禁止传入null。
     * @param <T>         对象类型。
     * @return Integer数值总和
     */
    public static <T> Integer rollUpInteger(Collection<T> col, Function<T, Integer> valueMapper) {
        Assert.notNull(valueMapper, "valueMapper");
        int total = 0;
        if (CollectionUtils.isEmpty(col)) {
            return total;
        }
        col.removeIf(Objects::isNull);
        for (T item : col) {
            Integer value = valueMapper.apply(item);
            total += Optional.ofNullable(value).orElse(0);
        }
        return total;
    }

}
